package com.tool.emulator.mos6502;

import com.tool.emulator.apple2.runtime.EmuState;
import com.tool.emulator.common.AccessUnit;
import com.tool.emulator.common.Emulator;
import com.tool.emulator.common.IODevice;
import com.tool.emulator.common.VirtualDevice;

/**
 * Env.java @author tl 2011-6-15
 */
public class BUS6502 extends VirtualDevice implements Constants6502 {
	public static final int UNUSED_UNIT_VALUE = 0x00;
	protected final CPU6502 cpu;
	protected final AccessUnit[] mem = new AccessUnit[1 << 16];
	protected final Emulator emulator = new Emulator();

	public BUS6502() {
		super("BUS6502");
		this.cpu = new CPU6502(this);
		this.initUnuse(0, mem.length);
		this.emulator.addDevice(this);
		this.emulator.addDevice(this.cpu);
	}

	@Override
	public void deviceSaveState(EmuState emuState) throws Exception {
		super.deviceSaveState(emuState);
		byte[] vals = new byte[mem.length];
		for (int i = 0; i < vals.length; i++)
			vals[i] = (byte) mem[i].directGet();
		emuState.putDeviceByteState(this, vals);
		//	map.put(AccessUnit.class, vals);
	}

	@Override
	public void deviceLoadState(EmuState emuState) throws Exception {
		super.deviceLoadState(emuState);
		byte[] vals = emuState.getDeviceByteState(this);
		//byte[] vals = (byte[]) map.get(AccessUnit.class);
		if (vals != null) {
			for (int i = 0; i < vals.length && i < mem.length; i++) {
				mem[i].directSet(0xff & (int) vals[i]);
			}
		}
	}

	public Emulator getEmulator() {
		return this.emulator;
	}

	public CPU6502 getCPU() {
		return this.cpu;
	}

	public void requestRESET() {
		cpu.interrupt(IR_RESET);
	}

	public void requestNMI() {
		cpu.interrupt(IR_NMI);
	}

	public void requestIRQ(int mask) {
		cpu.interrupt(mask & IRQ_MASK);
	}

	public void resetIRQ(int mask) {
		cpu.interruptReset(mask & IRQ_MASK);
	}

	public AccessUnit[] getMemory() {
		return this.mem;
	}

	public int read(int addr) {
		return mem[addr].directGet();
	}

	public int read2(int addr) {
		return read(addr) | (read(addr + 1) << 8);
	}

	public int read4(int addr) {
		return read2(addr) | (read2(addr + 2) << 16);
	}

	public void write(int addr, int val) {
		mem[addr].directSet(val);
	}

	public void readBytes(int addr, byte[] b) {
		this.readBytes(addr, b, 0, b.length);
	}

	public void readBytes(int addr, int[] b) {
		this.readBytes(addr, b, 0, b.length);
	}

	public void readBytes(int addr, byte[] b, int off, int len) {
		while (len-- > 0) {
			b[off++] = (byte) this.mem[addr++].directGet();
		}
	}

	public void readBytes(int addr, int[] b, int off, int len) {
		while (len-- > 0) {
			b[off++] = this.mem[addr++].directGet();
		}
	}

	public void writeBytes(int addr, int... bytes) {
		for (int i = 0; i < bytes.length; i++)
			write(addr++, bytes[i]);
	}

	public void writeBytes(int addr, byte... bytes) {
		for (int i = 0; i < bytes.length; i++)
			write(addr++, bytes[i]);
	}

	public void initRam(int addr, int size) {
		for (int i = 0; i < size; i++, addr++)
			initBus(addr, AccessUnit.newRam());
	}

	;

	public void initBus(int addr, AccessUnit unit) {
		mem[addr] = unit;
	}

	public void initIO(int addr, int len, IODevice device) {
		device.getClass();
		for (int i = 0; i < len; i++, addr++) {
			mem[addr] = AccessUnit.io(addr, device);
		}
	}

	public void setRom(int addr, int val) {
		initBus(addr, AccessUnit.rom(val));
	}

	public void initUnuse(int addr, int size) {
		for (int i = 0; i < size; i++, addr++)
			initBus(addr, AccessUnit.rom(UNUSED_UNIT_VALUE));
	}

	public void initRom(int addr, byte... bytes) {
		for (int i = 0; i < bytes.length; i++, addr++)
			setRom(addr, bytes[i]);
	}

	public void initRom(int addr, int... bytes) {
		for (int i = 0; i < bytes.length; i++, addr++)
			setRom(addr, bytes[i]);
	}
}
